package org.yaosheng.blog.aspect;

import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Created by yaosheng on 2023/6/17.
 */
@Component
public class LogPoolManager {

    private int MAX_QUEUE_SIZE = 100;
    // 批量进行插入的数量
    private int BATCH_SIZE = 10;
    // 线程睡眠时间
    private int SLEEP_TIME = 500;
    // 日志插入执行的最大的时间间隔
    private long MAX_EXE_TiME = 10000;
    // 创建一个单线程的线程池
    private ExecutorService logManagerThreadPool = Executors.newSingleThreadExecutor();
    // 创建一个定长的线程池，线程数量为（java虚拟机可用的处理器数量 * 2 + 20 ）
    private ExecutorService logWorkerThreadPool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2 + 20);
    // 阻塞队列，存放日志内容
    private BlockingQueue<Integer> queue;
    // Boolean 原子变量类
    private AtomicBoolean run = new AtomicBoolean(true);
    // 整型 原子变量类，记录阻塞队列中的任务数量
    private AtomicInteger logCount = new AtomicInteger(0);
    private long lastExecuteTime;

    @PostConstruct
    public void init(){
        // 基于链表的双向阻塞队列，在队列的两端都可以插入和移除元素，是线程安全的，多线程并发下效率更高
        queue = new LinkedBlockingDeque<Integer>(MAX_QUEUE_SIZE);
        lastExecuteTime = System.currentTimeMillis();
        logManagerThreadPool.execute(new Runnable() {
            public void run() {
                while(run.get()){
                    try {
                        Thread.sleep(SLEEP_TIME);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    // 满足存放了10个日志或满足时间间隔已经大于设置的最大时间间隔时  执行日志插入
                    if(logCount.get()>=BATCH_SIZE || (System.currentTimeMillis() - lastExecuteTime) > MAX_EXE_TiME){
                        if(logCount.get() > 0) {
                            List<Integer> list = new ArrayList<Integer> ();
                            /**
                             *  drainTo (): 一次性从BlockingQueue获取所有可用的数据对象（还可以指定获取数据的个数），
                             *  通过该方法，可以提升获取数据效率；不需要多次分批加锁或释放锁。
                             *  将取出的数据放入指定的list集合中
                             */
                            queue.drainTo(list);
                            logCount.set(0);
                        }
                        lastExecuteTime = System.currentTimeMillis();
                    }
                }
            }
        });
    }

    public void add(Integer num) {
        try {
            queue.put(num);
            logCount.incrementAndGet();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
